using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.IO;

static class Helpers
{

    public static bool CheckIsPortAvailable(int port) {

        IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
        TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

        // http://stackoverflow.com/questions/570098/in-c-how-to-check-if-a-tcp-port-is-available
        foreach (TcpConnectionInformation tcpi in tcpConnInfoArray) {
            if (tcpi.LocalEndPoint.Port == port) {
                return false;
            }
        }

        return true;
    }
    public static IEnumerable<T> Shuffle<T>(IEnumerable<T> seq, int seed) {
        var rand = new Random(seed);
        var arr = seq.ToArray();
        // todo: check
        for (int ii = arr.Length - 1; ii >= 0; ii--) {
            var temp = arr[ii];
            var to = rand.Next(0, ii + 1);
            arr[ii] = arr[to];
            arr[to] = temp;


        }

        return arr;

    }
    public static bool ToBool(string str) {
        int res;

        if (int.TryParse(str, out res))
            return Convert.ToBoolean(res);

        else return Convert.ToBoolean(str);
    }

    public static IEnumerable<T>
    ReadFiles<T>(string directory, Func<Dictionary<string, string>, T> ctor) {
        foreach (var path in Directory.GetFiles(directory, "*.txt")) {
            T yield;
            try {
                yield = ctor(ReadFile(path));
            }
            catch (Exception ex) {
                Log("File reading issue: " + path);
                Log(ex);
                continue;
            }
            yield return yield;
        }
    }

    public static Dictionary<string, string>
    ReadFile(string path) {

        var dict = new Dictionary<string, string>();
        foreach (string line in File.ReadAllLines(path)) {
            if (line.Trim() != "" && !line.StartsWith("#")) {
                var split = line.Split('=');
                var key = split[0];
                var value = split[1];
                key = key.Trim().ToLower();
                value = value.Trim().ToLower(); // <--
                dict[key] = value;
            }
        }

        return dict;
    }

    static public readonly int PID = Process.GetCurrentProcess().Id;

    static public bool CheckOtherInstancesActive() {

        // http://stackoverflow.com/questions/206323/how-to-execute-command-line-in-c-get-std-out-results
        var p = new Process();
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.FileName = "tasklist.exe";
        p.StartInfo.Arguments = "/FI \"USERNAME eq " + Environment.UserName + "\"";
        p.StartInfo.CreateNoWindow = true;
        p.Start();

        var me = Process.GetCurrentProcess();
        var me_name = me.ProcessName.ToUpper(); //Application. ...

        try { // premature optimization ...
            if (Process.GetProcessesByName(me_name).Length == 1)
                return false;
        }
        catch (InvalidOperationException) { }

        var seen_once = false;
        string str;
        while ((str = p.StandardOutput.ReadLine()) != null) {
            str = str.ToUpper();
            if (str.StartsWith(me_name + ".EXE ")) {
                if (seen_once)
                    return true;
                seen_once = true;
            }
        }

        return false;

    }

    public static string TrimUrlPathComponent(string url_path_component) {
        string file;
        if (url_path_component.Length > 0 && url_path_component.StartsWith("/")) {
            file = url_path_component.Substring(1);
        }
        else {
            file = url_path_component;
        }
        return file;
    }

    public static string GetPathFromUrlPathComponent(string url_path_component) {
        var trimmed = TrimUrlPathComponent(url_path_component);
        return GetStaticFilePathFromPath(trimmed);
    }

    public static string GetStaticFilePathFromPath(string path) {
        return Program.dir_static + "/" + path;
    }

    public static void Log(Exception ex) {
        Log(ex + "");

        var aex = ex as AggregateException;
        if (aex != null) {
            foreach (var ex2 in aex.InnerExceptions)
                Log(ex2);
            return;
        }

        if (ex.InnerException != null)
            Log(ex.InnerException);

    }
    public static void Log(string msg) {

        var ts = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        Console.WriteLine(ts + " " + msg);

        File.AppendAllText(Program.log_file, PID + " " + ts + " " + msg + "\n");
    }

    public static string GetProfileFromName(string name) {
        // todo - strip invalid file characters
        // todo - if result exists, append number 
        // John, John2, John3...

        return name.ToLower();

    }

    public static string GetNameFromProfile(string profile) {

        return GetUserInfo(profile).UserName;

    }


    public static IEnumerable<string> GetProfiles() {
        foreach (var dir in Directory.GetDirectories(Program.dir_profiles)) {
            var profile = Path.GetFileName(dir);
            // todo: validate profiles?
            if (File.Exists(dir + "/" + Program.file_profile)) {
                yield return profile;
            }
        }
    }

    public static string GetProfileDir(string profile) {
        return Program.dir_profiles + "/" + profile;
    }

    public static string GetProfileFile(string profile) {
        return GetProfileDir(profile) + "/" + Program.file_profile;
    }

    public static UserInfo GetUserInfo(string profile) {
        return new UserInfo(GetProfileFile(profile));
    }

    public static IEnumerable<FoodInfo> GetFoodInfos() {
        return ReadFiles(Program.dir_foods, _d => new FoodInfo(_d));
    }
    public static IEnumerable<ExerciseInfo> GetExerciseInfos() {
        return ReadFiles(Program.dir_exercises, _d => new ExerciseInfo(_d));
    }

    public static void InitProfile(string name, string profile, Dictionary<string, string> dict) {
        Directory.CreateDirectory(GetProfileDir(profile));
        var user = new UserInfo(dict);
        user.WriteToFile(GetProfileFile(profile));

    }
    public static string GetUrl(int port) {
        return "http://localhost:" + port + "/";
    }

    // http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net
    public static int GetTcpPort() {
        int port;
        TcpListener l = new TcpListener(IPAddress.Loopback, 0);

        try {
            l.Start();
            port = ((IPEndPoint)l.LocalEndpoint).Port;
        }
        finally {
            l.Stop();
        }

        return port;
    }

    public static string GetProfileParam(this HttpListenerRequest request) {
        return request.QueryString[Program.query_profile];
    }
}